package top.tenebrous.yzl.im.server;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import top.tenebrous.yzl.im.contants.RedisConstants;
import top.tenebrous.yzl.im.context.ImContext;
import top.tenebrous.yzl.im.entity.ImWebSocketProperties;
import top.tenebrous.yzl.im.enums.ChannelType;
import top.tenebrous.yzl.im.enums.MessageType;
import top.tenebrous.yzl.im.factory.colony.MessageQueueFactory;
import top.tenebrous.yzl.im.factory.message.MessageFactory;
import top.tenebrous.yzl.im.handler.MessageHandler;
import top.tenebrous.yzl.im.interceptor.ImWebsocketInterceptor;
import top.tenebrous.yzl.im.listener.ReceiverMessageHandler;
import top.tenebrous.yzl.im.message.AuthRequestMessage;
import top.tenebrous.yzl.im.message.MessageBody;
import top.tenebrous.yzl.im.message.MessageResponse;
import top.tenebrous.yzl.im.sender.AbstractSenderHandler;
import top.tenebrous.yzl.im.sender.RedisSenderHandler;
import top.tenebrous.yzl.im.utils.WebSocketUtils;

import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @author yazhang6
 */
@Slf4j
@Component
public class WebSocketChatHandlerServer extends TextWebSocketHandler {

    @Autowired
    private MessageFactory messageFactory;

    @Autowired
    private ImContext imContext;

    @Autowired
    private RedisSenderHandler redisSenderHandler;

    @Autowired
    private MessageQueueFactory messageQueueFactory;

    @Autowired
    private ImWebSocketProperties webSocketProperties;

    @Autowired
    private ReceiverMessageHandler receiverMessageHandler;

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        List<Map.Entry<ImWebsocketInterceptor, Integer>> sortList = imContext.handlerInterceptorSorted();
        // 执行扩展拦截
        for (Map.Entry<ImWebsocketInterceptor, Integer> entry : sortList) {
            entry.getKey().beforeImWebSocketOpenProcessor(session);
        }
        // 获取客户端信息
        String sessionNo = (String) session.getAttributes().get("sessionNo");
        String userId = (String) session.getAttributes().get("userId");
        log.info("用户id:{} sessionNo:{} 开始建立链接...", userId, sessionNo);
        AuthRequestMessage authRequestMessage = new AuthRequestMessage();
        authRequestMessage.setSessionNo(sessionNo);
        authRequestMessage.setUserId(userId);
        MessageHandler<AuthRequestMessage> messageHandler = messageFactory.getInstance(MessageType.AUTH);
        if (messageHandler == null) {
            return;
        }
        messageHandler.execute(session, authRequestMessage);
        // 后置处理
        for (Map.Entry<ImWebsocketInterceptor, Integer> entry : sortList) {
            entry.getKey().afterImWebSocketOpenProcessor(session);
        }
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {

        /*
         * 处理消息 进行广播
         *
         * 1、websocket接收消息
         * 2、封装数据
         * 3、广播至redis
         * 4、服务端监听
         * onMessage  ---->  redis
         *                     ^
         *                     |
         *              websocket server    -->   handler message --->  websocket client
         *
         */
        log.info("服务端接收到新的聊天消息:消息内容[{}]", JSON.toJSONString(message));
        // 判断单机还是集群
        if (webSocketProperties.isEnableColony()) {
            // 如果是集群  获取集群共享模式
            AbstractSenderHandler instance = messageQueueFactory.getProducerInstance(webSocketProperties.getColonyShareType());
            if (Objects.isNull(instance)) {
                throw new RuntimeException("集群模式下, 未注册[{}]类型的消息发送者, 请检查是否有相关实现已经实现AbstractSenderHandler接口");
            }
            instance.broadcastMessage(session, message);
        } else {
            // 单机模式
            receiverMessageHandler.handlerMessage(message.getPayload(), ChannelType.SEND_CHANNEL.getCode());
        }

    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {

        /*
         * 关闭连接，处理消息 进行广播
         *
         * 1、websocket接收消息
         * 2、封装数据
         * 3、广播至redis
         * 4、服务端监听
         * onMessage  ---->  redis
         *                     ^
         *                     |
         *              websocket server    -->   handler message --->  websocket client
         *
         */

        List<Map.Entry<ImWebsocketInterceptor, Integer>> sortList = imContext.handlerInterceptorSorted();
        // 执行扩展拦截
        for (Map.Entry<ImWebsocketInterceptor, Integer> entry : sortList) {
            entry.getKey().afterImWebSocketOnCloseProcessor(session, status);
        }
        WebSocketUtils.removeSession(session);
        MessageBody messageBody = new MessageBody();
        messageBody.setFrom((String) session.getAttributes().get("userId"));
        messageBody.setContent(JSON.toJSONString(status.getReason()));
        messageBody.setExtras(ChannelType.CLOSE_CHANNEL.getCode());
        String msg = WebSocketUtils.buildTextMessage(MessageType.PUT.getCode(), messageBody);
        TextMessage message = new TextMessage(msg);
        log.info("服务端接收到用户下线通知:消息内容[{}]", JSON.toJSONString(message));
        // 判断单机还是集群
        if (webSocketProperties.isEnableColony()) {
            // 如果是集群  获取集群共享模式
            AbstractSenderHandler instance = messageQueueFactory.getProducerInstance(webSocketProperties.getColonyShareType());
            if (Objects.isNull(instance)) {
                throw new RuntimeException("集群模式下, 未注册[{}]类型的消息发送者, 请检查是否有相关实现已经实现AbstractSenderHandler接口");
            }
            instance.broadcastMessage(session, message);
        } else {
            // 单机模式
            receiverMessageHandler.handlerMessage(message.getPayload(),ChannelType.CLOSE_CHANNEL.getCode());
        }
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        String sessionNo =(String) session.getAttributes().get("sessionNo");
        log.error("{}:发送websoket业务处理错误 错误信息:{}", sessionNo, exception.getMessage());
    }
}
